#include <cstdlib>
#include <cerrno>
#include <cstdio>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <iostream>
#include <sys/poll.h>
#include <sys/epoll.h>

#include "readConfig.h"
#include "signal.h"
#include "netDevice.h"
#include "unp.h"
#include "network.h"
#include "config.h"

using namespace std;

const char ConfigFile[]= "config";
static const int timeout = 1;
string netMode = "433M";	//or wifi

void sig_chld(int signo)
{
	pid_t pid;
	int stat;
	while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
		cout<<"child "<<pid<<" terminated"<<endl;
	return;
}

void plat2Node(int sockfd, int nodeFd)
{
	char cmdBuff[1000];
	/* recv cmd from plat */
	int res;

	struct pollfd fds[2];
	fds[0].fd = sockfd;
	fds[0].events = POLLIN;

	fds[1].fd = nodeFd;
	fds[1].events = POLLIN;

	while(true)
	{
		bzero(cmdBuff, sizeof(cmdBuff));
		res = TEMP_FAILURE_RETRY(poll(fds, 2, -1));
		if(res == 0)
		{
			cout<<"Select timeout"<<endl;
			continue;
		}
		else if(res < 0)
		{
			perror("poll error");
			exit(EXIT_FAILURE);
		}

		//Data from plat
		if((fds[0].revents & POLLIN))
		{
			res = TEMP_FAILURE_RETRY(recv(sockfd, cmdBuff, sizeof(cmdBuff), 0));
			if(res == 0)
			{
				cout<<"Plat close socket"<<endl;
				break;
			}
			else if(res < 0)
			{
				perror("recv error");
				exit(EXIT_FAILURE);
			}
			else
			{
				//cout<<"Gateway: Recv "<<res<<" bytes from plat"<<endl;
				Send(nodeFd, cmdBuff, res, 0);
			}
		}//if((fds[0].revents & POLLIN))

		//Data from node
		if((fds[1].revents & POLLIN))
		{
			res = TEMP_FAILURE_RETRY(recv(nodeFd, cmdBuff, sizeof(cmdBuff), 0));
			if(res > 0)
			{  
				//cout<<"Gateway: Recv "<<res<<" bytes from node"<<endl;
				Send(sockfd, cmdBuff, res, 0);
			}
			else if(res ==0)
			{
				cout<<"Disconneted from node"<<endl;
				break;
			}
			else
			{
				perror("recv error");
				exit(EXIT_FAILURE);
			}
		}//if((fds[1].revents & POLLIN))
	}
}

void* server433(void* null)
{
	int res;
	tcpServer server;
	server.Listen(FTTMServerPort);

	//cout<<"433M Server: Listen on port "<<ntohs(local.sin_port)<<endl;

	//Create 433M device
	FTTMDevice ndd;
	//ndd.on();
	ndd.setup();
	ndd.on();

	tcpSocket client;
	client.Connect("", netDeviceListenPort);
	int cli433fd = client.getSocket();

	const int MAX_EVENTS = 100;
	struct epoll_event ev, events[MAX_EVENTS];
	int epollfd = epoll_create(MAX_EVENTS);
	if(epollfd == -1)
	{
		perror("433M server epoll_create");
		exit(-1);
	}

	ev.events = EPOLLIN;
	ev.data.fd = server.getSocket();
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
	{
		perror("epoll_ctl: client.getsocket()");
		exit(-1);
	}

	ev.events = EPOLLIN;
	ev.data.fd = cli433fd;
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
	{
		perror("epoll_ctl: cli433fd");
		exit(-1);
	}

	char buff[254];

	int connfd, readn, nfds;

	struct sockaddr_in cliaddr;
	socklen_t slen = sizeof(cliaddr);
	char paddr[INET_ADDRSTRLEN];

	map<unsigned char, int> id;
	map<unsigned char, char>type;

	while(true)
	{
		nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
		if(nfds == -1)
		{
			perror("epoll_wait");
			exit(-1);
		}

		for(int i=0; i<nfds; ++i)
		{
			//Connect from gateway
			if(events[i].data.fd == server.getSocket())
			{
				connfd = TEMP_FAILURE_RETRY(accept(events[i].data.fd, (SA*)&cliaddr, &slen));
				if(connfd < 0)
				{
					perror("accept error");
					exit(EXIT_FAILURE);
				}
				cout<<"433M Server: Receive connect from: "<<inet_ntop(AF_INET, &cliaddr.sin_addr, paddr, INET_ADDRSTRLEN)<<":"<<ntohs(cliaddr.sin_port)<<endl;

				//New connection
				ev.events = EPOLLIN;
				ev.data.fd = connfd;
				res = TEMP_FAILURE_RETRY(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev));

				//Read and save id
				char initial[2];
				Readn(connfd, initial, 2);	//Read ID
				id[initial[0]] = connfd;	//ID
				type[initial[0]] = initial[1];	//type
				//cout<<"433M server: ID is "<<(int)rid<<endl;
			}
			//Data from 433M device
			else if(events[i].data.fd == cli433fd)
			{
				cout<<"433M server: Data from 433M device"<<endl;
				char len;
				char buff[22];
 
				res = Readn(cli433fd, &len, 1);
				if(0 == res)
				{
					Close(cli433fd);

					//Delete from epoll
					TEMP_FAILURE_RETRY(epoll_ctl(epollfd, EPOLL_CTL_DEL, cli433fd, &ev));
					//Delete from id map
					map<unsigned char, int>::iterator map_it = id.begin();
					while(map_it != id.end())
					{
						if(map_it->second == cli433fd)
							id.erase(map_it);
						++map_it;
					}

					continue;
				}

				cout<<"len: "<<dec<<(int)len<<endl;
				Readn(cli433fd, buff, len);
	
				if(buff[1] == DFLAG)
				{
					if(id.count(buff[0]))
					{
						Send(id[buff[0]], buff + 2, len - 2, 0); //Send data to plat
						cout<<"Gateway: Send "<<len - 2<<" bytes to plat"<<endl;
					}
					else
						cout<<"No such id"<<endl;
				}
				else if(buff[1] == CFLAG)
					cout<<"Control Msg"<<endl;
			}
			//Data from gateway
			else
			{
				cout<<"433M server: Data from gateway"<<endl;
				int currfd = events[i].data.fd;
				readn = Read(currfd, buff + 3, sizeof(buff) - 3);
				
				map<unsigned char, int>::iterator map_it = id.begin();
				while(map_it != id.end())
				{
					if(map_it->second == currfd)
						break;
					++map_it;
				}

				if(0 == readn)
				{
					Close(currfd);

					//Delete from epoll
					TEMP_FAILURE_RETRY(epoll_ctl(epollfd, EPOLL_CTL_DEL, currfd, &ev));
					//Delete from id map
					id.erase(map_it);
					continue;
				}

				buff[0] = (char)readn + 2;	//Send length
				buff[1] = map_it->first;	//ID
				buff[2] = type[map_it->first];	//ADD FLAG
				Send(cli433fd, buff, readn + 3, 0);	//Send data

				cout<<"433M server: Send "<<readn<<" bytes(id<1 byte> + data) to 433M device id: "<<(int)(map_it->first)<<endl;
			}//else
		}//for
	}//while

	return (void*)NULL;
}

void netM(int fromManagePlatM)
{
	tcpServer server;
	server.Listen(fromManagePlatM);

	udpClient udpServer;
	udpServer.Bind("127.0.0.1", networkMPort);

	const int MAX_EVENTS = 2;
	struct epoll_event ev, events[MAX_EVENTS];
	int epollfd = epoll_create(MAX_EVENTS);
	if(epollfd == -1)
	{
		perror("epoll_create");
		exit(-1);
	}

	//ADD control for tcp server
	ev.events = EPOLLIN;
	ev.data.fd = server.getSocket();
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
	{
		perror("epoll_ctl: server.getsocket()");
		exit(-1);
	}

	//Add control for udp server
	ev.data.fd = udpServer.getSocket();
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
	{
		perror("epoll_ctl: udpServer.getsocket()");
		exit(-1);
	}

	int nfds;
	while(true)
	{
		nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
		if(nfds == -1)
			break;
	}
}

int main(int argc, char** argv)
{
	Signal(SIGCHLD, sig_chld);
	Config addrConfigFile("wifiAddr.conf");

	//Start 433M server
	pthread_t thread433Server;
	Pthread_create(&thread433Server, NULL, server433, NULL);

	tcpServer server;
	server.Listen(fromMagagePlat);
	int fd = server.getSocket();

	cout<<"Gateway: Listen on port "<<fromMagagePlat<<endl;

	while(true)
	{
		struct sockaddr_in cliaddr;
		socklen_t clilen = sizeof(cliaddr);
		int connfd = TEMP_FAILURE_RETRY(accept(fd, (struct sockaddr *)&cliaddr, &clilen));
		char paddr[INET_ADDRSTRLEN];
		cout<<"Gateway: Receive connect from : "<<inet_ntop(AF_INET, &cliaddr.sin_addr, paddr, INET_ADDRSTRLEN)<<":"<<ntohs(cliaddr.sin_port)<<endl;
		if(Fork() == 0)  //in sub process
		{
			cout<<"new process"<<endl;
			Close(fd);

			//This is just for example
			unsigned char id;
			Recv(connfd, &id, 1, 0);
			//cout<<"ID:"<<(unsigned char)id<<endl;
			//printf("ID: %d\n", id);

			struct sockaddr_in nodeAddr;
			bzero(&nodeAddr, sizeof(nodeAddr));
			nodeAddr.sin_family = AF_INET;
			if(netMode == "wifi")
			{
				//port
				nodeAddr.sin_port = htons(toNode);

				//address
				string addrString;
				char idStr[4];
				sprintf(idStr, "%d", id);
				cout<<"Will connect to id: "<<idStr<<endl;
				addrString = addrConfigFile.Read(idStr, addrString);
				inet_pton(AF_INET, addrString.c_str(), &nodeAddr.sin_addr);
			}
			else if(netMode == "433M")
			{
				nodeAddr.sin_port = htons(FTTMServerPort);
				nodeAddr.sin_addr.s_addr = htons(INADDR_ANY);
			}
			else
			{
				cout<<netMode<<" is a bad net mode"<<endl;
				exit(1);
			}

			int nodefd = Socket(AF_INET, SOCK_STREAM, 0);
			if(connect(nodefd, (SA*)&nodeAddr, sizeof(nodeAddr)) < 0)
			{
				perror("connect to node error");
				goto exit_close;
			}
			char paddr[INET_ADDRSTRLEN];
			cout<<"Gateway: Connected to "<<inet_ntop(AF_INET, &nodeAddr.sin_addr, paddr, INET_ADDRSTRLEN)<<":"<<ntohs(nodeAddr.sin_port)<<endl;

			if(netMode == "433M")	//Tell 433M server which node does this socket want send to
				Send(nodefd, &id, 1, 0);

			plat2Node(connfd, nodefd);

exit_close:
			Close(nodefd);
			Close(connfd);
			exit(EXIT_SUCCESS);
		}//if((pid = fork()) == 0)
		Close(connfd);
	}//end while(true)
	Close(fd); //close listen fd

	return 0;
}//end main

